package com.dubbo;

import java.util.concurrent.CountDownLatch;

/**
 * @author caoqianhuan
 * @date 2022/9/20
 * @Description:
 */
public class InstructOrder {
    private int i = 0;

    public static void main(String[] args) throws InterruptedException {
        InstructOrder instructOrder = new InstructOrder();
        CountDownLatch countDownLatch = new CountDownLatch(2);
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                instructOrder.i++;
            }
            countDownLatch.countDown();
        });
        thread1.start();

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                instructOrder.i--;
            }
            countDownLatch.countDown();
        });
        thread2.start();
        countDownLatch.await();
        System.out.println(instructOrder.i);
    }
}

/**
 * 双重检测方法实现 单例模式；  这种方法在多线程模式下是不安全的
 * 由于JIT 指令重拍的优化所导致
 */
class Single {
    private static Single instance = null;

    private Single () {

    }
    public static Single getInstance() {
        if (instance == null) {
            synchronized (Single.class) {
                if (instance == null) {
                    instance = new Single();
                }
            }
        }
        return instance;
    }
}

/**
 *Classfile /D:/myyproject/dubbo-parent/jvm/src/main/java/com/dubbo/Single.class
 *   Last modified 2022-9-20; size 533 bytes
 *   MD5 checksum cc3091619e80758d86be7954b33935fd
 *   Compiled from "InstructOrder.java"
 * class com.dubbo.Single
 *   minor version: 0
 *   major version: 52
 *   flags: ACC_SUPER
 * Constant pool:
 *    #1 = Methodref          #5.#20         // java/lang/Object."<init>":()V
 *    #2 = Fieldref           #3.#21         // com/dubbo/Single.instance:Lcom/dubbo/Single;
 *    #3 = Class              #22            // com/dubbo/Single
 *    #4 = Methodref          #3.#20         // com/dubbo/Single."<init>":()V
 *    #5 = Class              #23            // java/lang/Object
 *    #6 = Utf8               instance
 *    #7 = Utf8               Lcom/dubbo/Single;
 *    #8 = Utf8               <init>
 *    #9 = Utf8               ()V
 *   #10 = Utf8               Code
 *   #11 = Utf8               LineNumberTable
 *   #12 = Utf8               getInstance
 *   #13 = Utf8               ()Lcom/dubbo/Single;
 *   #14 = Utf8               StackMapTable
 *   #15 = Class              #23            // java/lang/Object
 *   #16 = Class              #24            // java/lang/Throwable
 *   #17 = Utf8               <clinit>
 *   #18 = Utf8               SourceFile
 *   #19 = Utf8               InstructOrder.java
 *   #20 = NameAndType        #8:#9          // "<init>":()V
 *   #21 = NameAndType        #6:#7          // instance:Lcom/dubbo/Single;
 *   #22 = Utf8               com/dubbo/Single
 *   #23 = Utf8               java/lang/Object
 *   #24 = Utf8               java/lang/Throwable
 * {
 *   public static com.dubbo.Single getInstance();
 *     descriptor: ()Lcom/dubbo/Single;
 *     flags: ACC_PUBLIC, ACC_STATIC
 *     Code:
 *       stack=2, locals=2, args_size=0
 *          0: getstatic     #2                  // Field instance:Lcom/dubbo/Single;
 *          3: ifnonnull     37
 *          6: ldc           #3                  // class com/dubbo/Single
 *          8: dup
 *          9: astore_0
 *         10: monitorenter
 *         11: getstatic     #2                  // Field instance:Lcom/dubbo/Single;
 *         14: ifnonnull     27
 *         17: new           #3                  // class com/dubbo/Single
 *         new 一个对象是步骤执行的 由于JIT的优化 21 和 24 的顺序发生了交换 导致线程还未完成初始化就将引用赋值给了 instance变量
 *         其他线程进行第一个 if instance == null 判断时 发现instance不为空 直接返回一个 还未init 完的对象（半个对象） 造成程序的错误
 *         20: dup
 *         21: invokespecial #4                  // Method "<init>":()V
 *         24: putstatic     #2                  // Field instance:Lcom/dubbo/Single;
 *         27: aload_0
 *         28: monitorexit
 *         29: goto          37
 *         32: astore_1
 *         33: aload_0
 *         34: monitorexit
 *         35: aload_1
 *         36: athrow
 *         37: getstatic     #2                  // Field instance:Lcom/dubbo/Single;
 *         40: areturn
 *       Exception table:
 *          from    to  target type
 *             11    29    32   any
 *             32    35    32   any
 *       LineNumberTable:
 *         line 20: 0
 *         line 21: 6
 *         line 22: 11
 *         line 23: 17
 *         line 25: 27
 *         line 27: 37
 *       StackMapTable: number_of_entries = 3
 *         frame_type = 252 */